<?php
/**
 * Celebrio Database module
 * 
 * @copyright  Copyright (c) 2011 Celebrio Software
 * @package    Celebrio
 * @subpackage Database
 */
namespace Celebrio\Database;

use \dibi;

use Celebrio\Core\IModuleInit;
use Celebrio\Database\DatabaseInit;
use Celebrio\PasswordHasher;
use \DibiConnection;

/**
 * Azure initialization/installation routines.
 *
 * @author     Albireo Solutions
 * @package    Kernel
 */
class AzureInit extends DatabaseInit implements IDatabaseInit {
    
    private static $configForCreateLogin = null;
    
    public function init(array $settings = array()) {
        $settings["result"]["detectTypes"] = false;
        $settings["options"]["LoginTimeout"] = (int) $settings["options"]["LoginTimeout"];
        $settings['options']['MultipleActiveResultSets'] = false;
        parent::init($settings);
    }

    public function install(array $configuration = array()) {
        // storing config for the createUser method to be able to connect
        // to the master database later
        self::$configForCreateLogin = $configuration;
        parent::installDatabase($configuration);
    }
    
    public function isAvailable() {
        $available = true;
        if (!extension_loaded ( "sqlsrv" ) ) {
            $available = false;
        }
        return $available;
    }
    
    public function changeConfig(array $configuration) {
        
        //TODO: not sure whether port can be included as in documentation of sqlsrv
        $newConfig = array (
                "driver" => "azure",
                "host" => $configuration["host"],
                //"driver"=> "MSSQL2005"
                "options" => array(
                        // UID, pass and DB are set below
                        //"UID" => $configuration['username'] . '@' . $host[0],
                        // "PWD" => $configuration['password'],
                        // "Database" => $configuration['database'],
                        "LoginTimeout" => 5,
                        //"ReturnDatesAsStrings"=>false
                        )
            );
        self::setUser($newConfig, $configuration["username"]);
        self::setPassword($newConfig, $configuration["password"]);
        self::setDatabase($newConfig, $configuration["database"]);
        return $newConfig;
    }
    
    protected function databaseExists($dbname) {
        $query = dibi::query("IF db_id(%s) IS NOT NULL
                SELECT 1
                ELSE
                SELECT 0",$dbname );
        if($query->fetchSingle()==0) {
            $query->free();
            return false;
        } else {
            $query->free();
            return true;
        }
    }

    protected function tablesExist($dbname) {
        //TODO: azure tables exist doesnt work
//        $query = dibi::query("IF EXISTS (SELECT 1
//    FROM ".$dbname.".INFORMATION_SCHEMA.TABLES
//    WHERE TABLE_TYPE='BASE TABLE'
//    AND TABLE_NAME='users')
//        SELECT 1
//ELSE
//        SELECT 0 ");
//        if($query->fetchSingle()==0) {
//            $query->free();
//            return false;
//        } else {
//            $query->free();
//            return true;
//        }
        return false;
    }

    /**
     * Creates the login info in the azure database machine. That includes
     * connecting to the master db where LOGIN is created, then USER is
     * created in the actual database.
     * 
     * @param string $username
     * @param string $pass
     * @param boolean $newUser 
     */
    public function createLogin ($username, $pass, $newUser = true) {
        //TODO: nastavovanie prav v databazi
        $password = PasswordHasher::databaseHash($username, $pass);
        $masterConnection = $this->connectToMaster();
        if ($newUser) {
            $masterConnection->nativeQuery("CREATE LOGIN [$username] WITH PASSWORD = '$password';");//, CHECK_POLICY = OFF;");
            dibi::nativeQuery("CREATE USER [$username] FROM LOGIN [$username];");// WITH DEFAULT_SCHEMA = db_owner;");
            dibi::nativeQuery("EXEC sp_addrolemember 'db_ddladmin', [$username]; EXEC sp_addrolemember 'db_datareader', [$username]; EXEC sp_addrolemember 'db_datawriter', [$username];");
        } else {
            $masterConnection->nativeQuery("ALTER LOGIN [$username] WITH PASSWORD = '$password';");//, CHECK_POLICY = OFF;");
            // TODO isn't updating of the USER necessary?
        }
        $masterConnection->disconnect();
    }

    public function deleteLogin ( $username ) {
        dibi::nativeQuery("DROP USER [$username];");
        dibi::nativeQuery("DROP LOGIN [$username];");
    }
    
    
    
    
    protected static function getDatabase(array $configuration) {
        return $configuration["options"]["Database"];
    }
    
    protected static function setMasterDatabase(array &$configuration) {
        self::setDatabase($configuration, "master");
    }
    
    protected static function setDatabase(array &$configuration, $database) {
        $configuration["options"]["Database"] = $database;
    }
    
    protected static function setUser(array &$configuration, $user) {
        // if the username does not contain @, add the server name
        if (strpos($user, '@') === false) {
            $host = explode('.', $configuration["host"]);
            if (strpos($host[0], ':') !== false) {
                $host[0] = substr($host[0], strpos($host[0], ':')+1);
            }
            $user .= '@' . $host[0];
        }
        $configuration["options"]["UID"] = $user;
    }
    
    protected static function setPassword(array &$configuration, $password) {
        $configuration["options"]["PWD"] = $password;
    }
    
    /**
     * Connects to the master database with login information which can be:
     * 1) stored in the static variable of the class (this way is used when
     *    installing the Azure module.
     * OR
     * 2) loaded from the config file (not supported yet)
     * 
     * @todo complete the functionality for loading the config from Config Management
     * 
     * @return DibiConnection connection to the azure master database
     */
    private function connectToMaster() {
        if (self::$configForCreateLogin != null) {
            // the configuration was set while installing the database
            // (the config file hasn't been set yet)
            $config = self::$configForCreateLogin;
        } else {
            // load configuration from Config Management class
            $config = null;
            throw new \Exception("config not set up, not supported yet loading config from file.");
        }
        self::setDatabase($config, "master");
        return new DibiConnection($config);
    }

}
